﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Globalization;     //CultureInfo
using System.IO;
using System.Reflection;        //Assembly
using System.Resources;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using System.Timers;
using mighty.on;
using System.Net;
using System.Net.Sockets;
using System.Xml.Serialization;
using mighty;
using System.Media;
using log4net;
using log4net.Config;
using Mighty.Brain;
using Mighty.Common;
using Mighty.PlayingBoard;

namespace Mighty.Mighty
{
    public partial class Mighty : Form, IVisual, IDebug
    {

        MightyModule mightyModule = new MightyModule();
        Hashtable moduleHash = new Hashtable();

        String[] selectModuleList = new String[5];
        ActualStatus gameStatus = ActualStatus.GameStartEvent;
        String gameMsg;
        String summaryMsg;

        PlayingBoard.PlayingBoard playingBoard = new PlayingBoard.PlayingBoard();
        GameInfo gameInfo = new GameInfo();

        ILog log; 
        
        Thread gameThread = null;
        AutoResetEvent autoEvent = new AutoResetEvent(false);
        PlayerLocation[] location = new PlayerLocation[5];

        HandedCard[] throwedCards = new HandedCard[5];
        HandedCard throwingCard = null;
        int myPosition = 0;
        int choicedCardNum = 3;
        bool myAiUsed = false;

        Point pointCardGoal;
        int nowAlpha = 0;

        bool gameStart = false;
        Rectangle boardSize;

        OnlinePlayer myInfo = new OnlinePlayer();
        int roomNo = -1;
        Service webService = new Service();
        GameService gameService = new GameService();
        OnlinePlayer ownerInfo = new OnlinePlayer();

        Thread netThread = null;
        Network network = new Network();
        Thread th = null;
        Socket server = null;
        Socket client = null;
        List<Client> clients = new List<Client>();

        SoundPlayer soundCardDrop = new SoundPlayer("sound\\carddrop.wav");
        SoundPlayer soundGetPoint = new SoundPlayer("sound\\point.wav");
        SoundPlayer soundShuffle = new SoundPlayer("sound\\shuffle.wav");
        SoundPlayer soundApplause = new SoundPlayer("sound\\Applause.wav");
        SoundPlayer soundScream = new SoundPlayer("sound\\scream.wav");

        public Config config = new Config();

        String talkMsg_str;
        int talkMsg_position;

        System.Timers.Timer openingTimer = new System.Timers.Timer(20);
        System.Timers.Timer throwingTimer = new System.Timers.Timer(30);
        System.Timers.Timer pointCardTimer = new System.Timers.Timer(30);

        public Mighty()
        {
            InitializeComponent();

            InitMighty();

            XmlConfigurator.Configure(new System.IO.FileInfo("log4net.xml"));
            log = LogManager.GetLogger(typeof(Mighty));
        }

        public Mighty(OnlinePlayer m, int no)
        {
            InitializeComponent();

            InitMighty();

            myInfo = m;
            roomNo = no;

            try
            {
                webService.JoinRoom(no, m.ip, m.port, m.id);
                gameService.playerList = webService.ShowConditionPlayerList("where room_no = " + roomNo + " order by is_playing desc, entertime;");
                ownerInfo = (OnlinePlayer)webService.GetOwner(roomNo);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }


            if (myInfo.id == ownerInfo.id)
            {
                btStart.Text = "Start";
                showBtStart = true;
                UpdateBtStart();
                playingBoard.gameMode = GameMode.Server;
            }
            else
            {
                showBtStart = false;
                UpdateBtStart();
                playingBoard.gameMode = GameMode.Client;
            }

            string str;
            listPlayer.Items.Clear();

            foreach (OnlinePlayer op in gameService.playerList)
            {
                str = "";
                if (op.isPlaying == true) str += "(참여)";
                else str += "(대기)";

                str += op.id;
                if (op.id == ownerInfo.id)
                    str += "<방장>";

                listPlayer.Items.Add(str);
            }

            if (playingBoard.gameMode == GameMode.Server) StartServer();   // 서버라면 서버 시작 
            else if (playingBoard.gameMode == GameMode.Client) Connect(ownerInfo.ip);  // 클라이언트라면 서버 접속
        }

        private void InitMighty()
        {
            playingBoard.iDebug = this;
            playingBoard.iVisual = this;
            playingBoard.gameMode = GameMode.Offline;

            // board
            SetLocation();
            boardSize = new Rectangle(0, 0, 750, 730);  // 게임판 사이즈
            btStart.Focus();

            // timer
            openingTimer.Elapsed += new ElapsedEventHandler(OpeningTimer_Tick);
            throwingTimer.Elapsed += new ElapsedEventHandler(ThrowTimer_Tick);
            pointCardTimer.Elapsed += new ElapsedEventHandler(PointCardTimer_Tick);

            // dll
            mightyModule.LoadDLL();
            comboMyAI.Items.Clear();
            comboAI1.Items.Clear();
            comboAI2.Items.Clear();
            comboAI3.Items.Clear();
            comboAI4.Items.Clear();
            foreach (ModuleInfo module in mightyModule.moduleHash.Values)
            {
                comboMyAI.Items.Add(module.moduleID);
                comboAI1.Items.Add(module.moduleID);
                comboAI2.Items.Add(module.moduleID);
                comboAI3.Items.Add(module.moduleID);
                comboAI4.Items.Add(module.moduleID);
            }
            config = ConfigForm.getConfig();
            comboMyAI.Text = config.AI[0];
            comboAI1.Text = config.AI[1];
            comboAI2.Text = config.AI[2];
            comboAI3.Text = config.AI[3];
            comboAI4.Text = config.AI[4];

            tabControl1.SelectTab(1);
        }

        private void SetLocation()
        {
            for (int i = 0; i < 5; i++)
            {
                if (i == 0) location[i] = new PlayerLocation(0);
                else
                {
                    location[i] = new PlayerLocation(i);
                }
            }
            enableCombo1 = true;
            enableCombo2 = true;
            enableCombo3 = true;
            enableCombo4 = true;

            UpdateCombo1();
            UpdateCombo2();
            UpdateCombo3();
            UpdateCombo4();
        }

        #region Update controls

        private void UpdateMyAIUsed()
        {
            if (chkAiUsed.InvokeRequired) Invoke(new MethodInvoker(UpdateMyAIUsed));
            else myAiUsed = chkAiUsed.Focused;
        }

        private bool showChatView = true;
        private void UpdateChatView()
        {
            if (!showChatView) return;
            if (gameThread == null) return;
            if (txtChatView.InvokeRequired)
            {
                Invoke(new MethodInvoker(UpdateChatView));
            }
            else
            {
                txtChatView.Text = gameMsg;
                txtChatView.Select(txtChatView.TextLength, 0);
                txtChatView.ScrollToCaret();
            }
        }
        
        private bool showBtStart = false;
        private void UpdateBtStart()
        {
            if (btStart.InvokeRequired)
            {
                Invoke(new MethodInvoker(UpdateBtStart));
            }
            else
            {
                if (showBtStart)
                {
                    btStart.Visible = true;
                    btStart.Focus();
                }
                else
                {
                    btStart.Visible = false;
                }
            }
        }

        private bool enableCombo1 = false;
        private void UpdateCombo1()
        {
            if (comboAI1.InvokeRequired) Invoke(new MethodInvoker(UpdateCombo1));
            else
            {
                comboAI1.Location = location[1].combo;
                comboAI1.Enabled = enableCombo1;
            }
        }

        private bool enableCombo2 = false;
        private void UpdateCombo2()
        {
            if (comboAI2.InvokeRequired) Invoke(new MethodInvoker(UpdateCombo2));
            else
            {
                comboAI2.Location = location[2].combo;
                comboAI2.Enabled = enableCombo2;
            }
        }

        private bool enableCombo3 = false;
        private void UpdateCombo3()
        {
            if (comboAI3.InvokeRequired) Invoke(new MethodInvoker(UpdateCombo3));
            else
            {
                comboAI3.Location = location[3].combo;
                comboAI3.Enabled = enableCombo3;
            }
        }

        private bool enableCombo4 = false;
        private void UpdateCombo4()
        {
            if (comboAI4.InvokeRequired) Invoke(new MethodInvoker(UpdateCombo4));
            else
            {
                comboAI4.Location = location[4].combo;
                comboAI4.Enabled = enableCombo4;
            }
        }

        private bool showBtChoiceOK = false;
        private void UpdateBtChoiceOK()
        {
            if (btChoiceOK.InvokeRequired) Invoke(new MethodInvoker(UpdateBtChoiceOK));
            else btChoiceOK.Visible = showBtChoiceOK;
        }

        private bool enableComboMyAI = false;
        private void UpdateComboMyAI()
        {
            if (comboMyAI.InvokeRequired) Invoke(new MethodInvoker(UpdateComboMyAI));
            else comboMyAI.Enabled = enableComboMyAI;
        }

        private bool enableChkAI = false;
        private void UpdateChkAi()
        {
            if (chkAi.InvokeRequired) Invoke(new MethodInvoker(UpdateChkAi));
            else chkAi.Enabled = enableChkAI;
        }

        private bool enableChkAIUsed = false;
        private void UpdateChkAiUsed()
        {
            if (chkAi.InvokeRequired) Invoke(new MethodInvoker(UpdateChkAiUsed));
            else chkAiUsed.Enabled = enableChkAIUsed;
        }

        #endregion

        public bool Connect(string ip)
        {
            try
            {
                IPEndPoint ipep = new IPEndPoint(IPAddress.Parse(ip), ownerInfo.port);  // 채팅 메인 서버 접속
                Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

                //    Client client = new Client(
                socket.Connect(ipep);


                DebugMsgAdd(ip + "서버에 접속 성공...");

                th = new Thread(new ThreadStart(Receive));
                th.Start();

                return true;
            }
            catch (Exception ex)
            {
                DebugMsgAdd(ex.Message);
                return false;
            }
        }

        private void ClickbtStart()
        {
            menuConfig.Enabled = false;
            chkAi.BackColor = SystemColors.Control;
            chkAiUsed.BackColor = SystemColors.Control;

            talkMsg_str = "";

            if (gameThread != null)
            {
                gameThread.Abort();
                gameThread = null;
            }

            // 이전에 게임을 시작한 적이 있다면
            if (gameStart == true)
            {
                gameStart = false;
                choicedCardNum = 3;

                talkMsg_str = "";

                InitAllThrowCard();
                playingBoard.Init();
                playingBoard.Shuffle();

                soundApplause.Stop();
            }

            selectModuleList[0] = comboMyAI.Text;
            selectModuleList[1] = comboAI1.Text;
            selectModuleList[2] = comboAI2.Text;
            selectModuleList[3] = comboAI3.Text;
            selectModuleList[4] = comboAI4.Text;

            enableComboMyAI = false;
            enableCombo1 = false;
            enableCombo2 = false;
            enableCombo3 = false;
            enableCombo4 = false;
            enableChkAI = false;
            enableChkAIUsed = false;

            UpdateComboMyAI();
            UpdateCombo1();
            UpdateCombo2();
            UpdateCombo3();
            UpdateCombo4();
            UpdateChkAi();
            UpdateChkAiUsed();

            tsBettingInfo.Text = "게임시작";

            gameThread = new Thread(RequestThread);
            gameThread.Start();

        }

        private void btStart_Click(object sender, EventArgs e)
        {
            ClickbtStart();
        }

        private void RequestThread()
        {
            if (InitGame()) playingBoard.StartGame();
            gameThread = null;
        }

        public void SetMyGameInfo(GameInfo g)
        {
            HandedCard[] hcs = new HandedCard[13];
            gameInfo = g;

            if (gameInfo.myCards.Length > 1)
            {
                hcs = MakeCardGroup(gameInfo.myCards);
                gameInfo.myCards = hcs;
            }
        }

        public bool InitGame()
        {
            try
            {
                if (playingBoard.gameMode == GameMode.Server)
                {
                    if (clients.Count >= 4)
                    {
                        int i = 1;
                        playingBoard.SetPlayer(myInfo.id, 0);
                        foreach (Client c in clients)
                        {
                            if (i == 5) break;
                            i++;
                        }
                    }
                    else
                    {
                        MessageBox.Show("플레이어가 적어서 게임을 시작할 수 없습니다.");
                        return false;
                    }
                }
                else
                {
                    ModuleInfo m;
                    for (int i = 0; i < 5; i++)
                    {
                        String selectModule = selectModuleList[i];
                        m = (ModuleInfo)mightyModule.moduleHash[selectModule];

                        if (i == myPosition && !myAiUsed) playingBoard.SetPlayer(myInfo.id, i, config.nick[i], config.score[i]);
                        else playingBoard.SetAI(m.moduleName, i, m.moduleType, config.nick[i], config.score[i]);
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
                return false;
            }

            showBtStart = false;
            showBtChoiceOK = false;
            UpdateBtStart();
            UpdateBtChoiceOK();
            return true;
        }

        public void StartServer()
        {
            //서버 시작
            try
            {
                netThread = new Thread(new ThreadStart(AcceptClient));
                netThread.IsBackground = true;
                netThread.Start();
            }
            catch (Exception ex)
            {
                DebugMsgAdd(ex.Message);
            }
        }

        public void AcceptClient()
        {
            try
            {
                IPEndPoint ipep = new IPEndPoint(IPAddress.Any, ownerInfo.port);
                server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                server.Bind(ipep);
                server.Listen(20);
                Client tmpClient = null;

                DebugMsgAdd("클라이언트 접속 대기중...");
                while (true)
                {
                    Socket socket = server.Accept();
                    IPEndPoint ip = (IPEndPoint)socket.RemoteEndPoint;
                    DebugMsgAdd(ip.Address + "접속...");

                    if (socket.Connected)
                    {
                        tmpClient = new Client(webService.GetClientID(ip.Address.ToString()), socket);
                        clients.Add(tmpClient);

                        SendData(Encoding.Default.GetBytes("접속 성공^_^V"), client);
                    }

                }
            }
            catch (Exception ex)
            {
                DebugMsgAdd(ex.Message);
            }
        }

        private void SendData(byte[] data, Socket client)
        {
            try
            {
                int total = 0;
                int size = data.Length;
                int left_data = size;
                int send_data = 0;

                // 전송할 실제 데이터의 크기 전달
                byte[] data_size = new byte[4];
                data_size = BitConverter.GetBytes(size);
                send_data = client.Send(data_size);

                // 실제 데이터 전송
                while (total < size)
                {
                    send_data = client.Send(data, total, left_data, SocketFlags.None);
                    total += send_data;
                    left_data -= send_data;
                }
            }
            catch (Exception ex)
            {
                DebugMsgAdd(ex.Message);
            }
        }

        public void DebugMsgClear()
        {
            gameMsg = "";
            UpdateChatView();
        }

        public void DebugMsgAdd(String msg)
        {
            gameMsg += "\r\n" + msg;
            UpdateChatView();

        }

        public void SummaryMsgClear()
        {
            summaryMsg = "";
            UpdateChatView();
        }

        public void SummaryMsgAdd(String msg)
        {
            summaryMsg += "\r\n" + msg;
            UpdateChatView();
        }


        private void ShowMyInfo(Graphics drawingSurface, StringFormat myFormat)
        {
            drawingSurface.DrawString("[내정보] oioio11", new Font("Arial", 9), Brushes.Black, new Point(760, 30), myFormat);
            // drawingSurface.DrawString("오늘의 승률 : 90% ", new Font("Arial", 9), Brushes.Black, new Point(760, 50), myFormat);
            //  drawingSurface.DrawString("(누적승률 : 71%) ", new Font("Arial", 9), Brushes.Black, new Point(760, 70), myFormat);

            drawingSurface.DrawString("[자동 플레이 설정]", new Font("Arial", 9), Brushes.Black, new Point(760, 100), myFormat);

        }

        # region Drawing

        /// <summary>
        /// Invalidate가 호출될때마다 화면을 다시 그린다.
        ///  * 플레이어의 이름을 그린다
        ///  * 카드와 이동하는 카드를 그린다
        ///  * 메세지를 그린다
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Mighty_Paint(object sender, PaintEventArgs e)
        {
            // 표면 만들기
            Graphics drawingSurface = e.Graphics;
            if (log.IsDebugEnabled) log.Debug(String.Format("    Mighty_Paint {0}", gameStatus));

            DrawPlayerName(drawingSurface);                                 // 플레이어 이름 보여주기
            if ((int)gameStatus > (int)ActualStatus.GameStartEvent)
            {
                DrawThrowedCard(drawingSurface);
                DrawPointCard(drawingSurface);                              // 플레이어가 먹은 그림카드 보여주기
                DrawHandCard(drawingSurface);
                DrawTalkMsg(drawingSurface);
                if (gameStatus == ActualStatus.RoundCardThrowEvent) DrawThrowingCard(drawingSurface);
                if (gameStatus == ActualStatus.BettingTalkEvent) DrawBettingTalk(drawingSurface);
                //WriteStr(drawingSurface, Convert.ToString(timeout), new Point(30,30), StrType.shadow);
                //WriteStr(drawingSurface, String.Format("{0}", playerState), new Point(50, 30), StrType.shadow);
                //ShowMyInfo(drawingSurface, myFormat);                       // 내 정보 보여주기
            }

        }

        /// <summary>
        /// 플레이어이름 그리기
        /// </summary>
        /// <param name="drawingSurface"></param>
        private void DrawPlayerName(Graphics drawingSurface)
        {
            string str;
            for (int i = 0; i < 5; i++)
            {

                str = "";
                if (gameInfo.attackPlayer != null)
                {
                    if (i == gameInfo.attackPlayer.position && gameStatus >= ActualStatus.BettingCompleteEvent)
                        str += "(주공) ";
                }

                if (gameInfo.friend != null)
                {
                    if (i == gameInfo.friend.position && gameInfo.friend.founded == true)
                        str += "(프렌드) ";
                }

                //str += string.Format("{0}번 {1} : {2}점", i, config.nick[i], config.score[i]);
                if (gameInfo.players[i] != null)
                    str += string.Format("{0}번 {1} : {2}점", i, gameInfo.players[i].nick, gameInfo.players[i].score);

                // 플레이어 이름 보여주기
                if (i == 0)
                {
                    WriteStr(drawingSurface, "          " + str, new Point(location[i].name.X, location[i].name.Y), StrType.shadow);
                }
                else
                {
                    WriteStr(drawingSurface, str, new Point(location[i].name.X, location[i].name.Y - 20), StrType.shadow);
                }

            }
            comboAI1.Visible = true;
            comboAI2.Visible = true;
            comboAI3.Visible = true;
            comboAI4.Visible = true;
        }

        /// <summary>
        /// 화면에 글씨 쓰기
        /// </summary>
        /// <param name="drawingSurface"></param>
        /// <param name="str"></param>
        /// <param name="point"></param>
        /// <param name="type"></param>
        private void WriteStr(Graphics drawingSurface, String str, Point point, StrType type)
        {

            StringFormat myFormat = new StringFormat();

            switch (type)
            {
                case StrType.shadow:
                    drawingSurface.DrawString(str, new Font("arial", 9), Brushes.Black, point, myFormat);
                    drawingSurface.DrawString(str, new Font("arial", 9), Brushes.White, new Point(point.X - 1, point.Y - 1), myFormat);
                    break;
                case StrType.timer:
                    drawingSurface.DrawString(str, new Font("arial", 20), Brushes.Black, point, myFormat);
                    break;
                case StrType.normal:
                default:
                    drawingSurface.DrawString(str, new Font("arial", 9), Brushes.Black, point, myFormat);
                    break;
            }
        }

        /// <summary>
        /// 플레이어가 먹은 그림카드 그리기
        /// </summary>
        /// <param name="drawingSurface"></param>
        private void DrawPointCard(Graphics drawingSurface)
        {
            if ((int)gameStatus > (int)ActualStatus.ChoiceFriendTalkEvent)
            {
                int i = 0;
                int j = 0;
                Point tmpPoint = new Point();
                List<RoundCard> scoreCardList = new List<RoundCard>();

                foreach (Player p in gameInfo.players)
                {
                    i = 0;
                    j = 0;
                    scoreCardList.Clear();
                    if ( p.pointCard != null ) scoreCardList.AddRange(p.pointCard);
                    foreach (Card c in scoreCardList)
                    {
                        tmpPoint.X = location[p.position].scoreCard.X + i * 20;
                        tmpPoint.Y = location[p.position].scoreCard.Y + j * 20;
                        DrawCard(drawingSurface, c, tmpPoint);
                        i++;
                        if (i == 8)
                        {
                            i -= 8;
                            j++;
                        }
                    }
                }
            }
        }

        /// <summary>
        /// 버린카드 그리기
        /// </summary>
        /// <param name="drawingSurface"></param>
        private void DrawThrowedCard(Graphics drawingSurface)
        {
            if ((int)gameStatus > (int)ActualStatus.ChoiceFriendTalkEvent)
            {
                int position = 0;
                if (gameInfo.roundIdx > 0 ) position = gameInfo.roundHistory[gameInfo.roundIdx-1].winner;

                for (int i = 0; i < 5; i++)
                {
                    if (throwedCards[position] != null)
                    {
                        //if (gameStatus == ActualStatus.RoundCardThrowEvent) DrawCard(drawingSurface, nowThrowedCards[tmp], location[tmp].nowThrowCard, nowAlpha);
                        if (gameStatus == ActualStatus.RoundCardThrowEvent) DrawCard(drawingSurface, throwedCards[position], location[position].throwingCard);
                        else DrawCard(drawingSurface, throwedCards[position], location[position].throwingCard);

                    }
                    position = (position + 1) % 5;
                }
            }
        }

        /// <summary>
        /// 버리는카드 그리기 
        /// </summary>
        /// <param name="drawingSurface"></param>
        private void DrawThrowingCard(Graphics drawingSurface)
        {
            if (throwingCard == null) return;

            int position = gameInfo.round.cardStack[gameInfo.roundCardIdx].owner;
            DrawCard(drawingSurface, throwingCard, location[position].throwingCard);
        }

        /// <summary>
        /// Talk 메세지를 그린다
        /// </summary>
        /// <param name="drawingSurface"></param>
        private void DrawTalkMsg(Graphics drawingSurface)
        {
            string imgName = "";
            Point point = new Point();
            Point strPoint = new Point();
            StrType strType = StrType.normal;


            if (talkMsg_str != null && talkMsg_str != "")
            {
                if (talkMsg_position == -1)
                {
                    imgName = "bg_msg";
                    point.X = 260;
                    point.Y = 200;
                    strType = StrType.shadow;
                }
                else
                {
                    point.X = location[talkMsg_position].name.X;
                    point.Y = location[talkMsg_position].name.Y;
                    strType = StrType.normal;
                }

                strPoint.X = point.X + 10;
                strPoint.Y = point.Y + 50;

                switch (talkMsg_position)
                {

                    case 0:
                        imgName = "talkdown";
                        point.Y += 40;
                        strPoint.X = point.X + 10;
                        strPoint.Y = point.Y + 10;
                        break;
                    case 1:
                        imgName = "talkup";
                        point.Y += 30;
                        break;
                    case 2:
                        imgName = "talkup";
                        point.Y += 30;
                        break;
                    case 3:
                        imgName = "talkup";
                        point.Y += 30;
                        break;
                    case 4:
                        imgName = "talkup";
                        point.Y += 30;
                        break;
                }

                Image tmpImg = new Bitmap(Resources.GetImage(imgName));

                drawingSurface.DrawImage(tmpImg, point);
                WriteStr(drawingSurface, talkMsg_str, strPoint, strType);
            }
        }

        /// <summary>
        /// Betting 메세지를 그린다
        /// </summary>
        /// <param name="drawingSurface"></param>
        private void DrawBettingTalk(Graphics drawingSurface)
        {
            string imgName = "";
            Point point = new Point();
            Point strPoint = new Point();

            Betting lastBetting = gameInfo.bettingHistory[gameInfo.bettingHistory.Count - 1];
            if (lastBetting != null)
            {
                point.X = location[lastBetting.position].name.X;
                point.Y = location[lastBetting.position].name.Y;

                strPoint.X = point.X + 10;
                strPoint.Y = point.Y + 50;

                switch (lastBetting.position % 5)
                {
                    case 0:
                        imgName = "talkdown";
                        point.Y += 40;
                        strPoint.X = point.X + 10;
                        strPoint.Y = point.Y + 10;
                        break;
                    case 1:
                        imgName = "talkup";
                        point.Y += 30;
                        break;
                    case 2:
                        imgName = "talkup";
                        point.Y += 30;
                        break;
                    case 3:
                        imgName = "talkup";
                        point.Y += 30;
                        break;
                    case 4:
                        imgName = "talkup";
                        point.Y += 30;
                        break;

                }
                string strTalk = "패스";
                if (!lastBetting.isPass) strTalk = lastBetting.giruda.ToString() + " " + lastBetting.limit;

                Image tmpImg = new Bitmap(Resources.GetImage(imgName));

                drawingSurface.DrawImage(tmpImg, point);
                WriteStr(drawingSurface, strTalk, strPoint, StrType.normal);
            }
        }

        /// <summary>
        /// 나의 카드 그리기
        ///  * 상황에 따라서 적절한 그리기를 선택한다
        ///  * BettingReadyEvent 일때는 opening을 그린다
        ///  * ChoiceCardEvent 일때는 카드 선택을 그린다
        /// </summary>
        /// <param name="drawingSurface"></param>
        private void DrawHandCard(Graphics drawingSurface)
        {
            if (gameStatus == ActualStatus.BettingReadyEvent) DrawOpeningCard(drawingSurface);
            else if (gameStatus == ActualStatus.ChoiceCardEvent) DrawChoiceCard(drawingSurface);
            else DrawRoundCard(drawingSurface);
        }

        /// <summary>
        /// 오프닝 시 위에서 떨어지는 내 카드 그리기
        /// </summary>
        /// <param name="drawingSurface"></param>
        private void DrawOpeningCard(Graphics drawingSurface)
        {
            GraphicsState previousState = drawingSurface.Save();
            for (int i = 0; i < 10; i++)
            {
                HandedCard c = gameInfo.myCards[i];

                int tmpY = location[myPosition].openingHandedCard.Y - i * 40;
                if (tmpY >= location[myPosition].handedCard.Y) tmpY = location[myPosition].handedCard.Y;

                DrawCard(drawingSurface, c, new Point(location[myPosition].openingHandedCard.X + i * 20, tmpY));

                // 마지막 카드가 끝에 도착했다면 타이머 종료
                if (i == 9 && tmpY == location[myPosition].handedCard.Y) openingTimer.Stop();
            }
        }

        /// <summary>
        /// 3장의 추가카드를 포함한 내카드 그리기
        /// </summary>
        /// <param name="drawingSurface"></param>
        private void DrawChoiceCard(Graphics drawingSurface)
        {
            GraphicsState previousState = drawingSurface.Save();
            int tmpOffset = 0;

            for (int i = 0; i < gameInfo.myCards.Length; i++)
            {
                if (gameInfo.myCards[i] != null)
                {
                    if (gameInfo.myCards[i].cmdRemove == true) tmpOffset = 20;
                    else tmpOffset = 0;

                    DrawCard(drawingSurface, gameInfo.myCards[i], new Point(location[myPosition].handedCard.X + i * 20, location[myPosition].handedCard.Y - tmpOffset));
                }
            }
        }

        /// <summary>
        /// 사용안한 카드를 그리기
        /// </summary>
        /// <param name="drawingSurface"></param>
        private void DrawRoundCard(Graphics drawingSurface)
        {
            GraphicsState previousState = drawingSurface.Save();
            int tmpOffset = 0;
            int cnt = 0;
            for ( int i = 0 ; i < 10 ; i++ ) {
                HandedCard c = gameInfo.myCards[i];

                if (c == null) continue;
                if (c.isUsed) continue;

                if (gameStatus == ActualStatus.RoundTicEvent) {
                    if (c.isPossible) tmpOffset = 20;
                    else tmpOffset = 0;
                } 
                DrawCard(drawingSurface, c, new Point(location[myPosition].handedCard.X + cnt * 20, location[myPosition].handedCard.Y - tmpOffset));
                cnt++;
            }
        }


        private void DrawCard(Graphics drawingSurface, Card card, Point point)
        {
            int tmpIndex = GetCardIndex(card);
            if (tmpIndex != -1)
            {
                Image tmpCard = new Bitmap(Resources.GetImage(String.Format("_" + Convert.ToString(tmpIndex))));
                drawingSurface.DrawImage(tmpCard, point.X, point.Y);
            }
        }

        private void DrawCard(Graphics drawingSurface, Card card, Point point, int alpha)
        {
            int tmpIndex = GetCardIndex(card);
            if (tmpIndex != -1)
            {
                Image tmpCard = new Bitmap(Resources.GetImage(String.Format("_" + Convert.ToString(tmpIndex))));
                drawingSurface.DrawImage((Image)SetAlphaOfImage((Bitmap)tmpCard, 50), point.X, point.Y);
            }
        }

        public Bitmap SetAlphaOfImage(Bitmap img, int alpha)
        {
            Bitmap tmpImage = new Bitmap(img.Width, img.Height);
            for (int y = 0; y < img.Height; y++)
            {
                for (int x = 0; x < img.Width; x++)
                {
                    Color temp = Color.FromArgb(alpha, img.GetPixel(x, y));
                    tmpImage.SetPixel(x, y, temp);
                }
            }
            return tmpImage;
        }

        # endregion

        /// <summary>
        /// ThrowingCard에서 사용하는 전역변수
        /// </summary>
        Point throwingGap = new Point();

        /// <summary>
        /// 각 라운드에서 선택된 카드를 낸다
        /// </summary>
        /// <param name="position"></param>
        /// <param name="hc"></param>
        public void ThrowCard(RoundCard card)
        {
            HandedCard h = new HandedCard();
            h.shape = card.shape;
            h.number = card.number;
            h.isUsed = true;
            throwingCard = h;

            int position = gameInfo.round.cardStack[gameInfo.roundCardIdx].owner;
            throwingGap.X = (location[position].throwedCard.X - location[position].throwingCard.X) / 5;
            throwingGap.Y = (location[position].throwedCard.Y - location[position].throwingCard.Y) / 5;

            throwingTimer.Start();
        }

        /// <summary>
        /// Talk 메세지를 설정한다
        ///  * 이후 Invalidate를 호출해서 메세지가 화면에 나타난다
        /// </summary>
        /// <param name="str"></param>
        /// <param name="position">플레이어의 위치. -1이면 한가운데 출력한다</param>
        public void SetTalkMsg(String str, int position)
        {
            talkMsg_str = str;
            talkMsg_position = position;
        }

        private void Mighty_Load(object sender, EventArgs e)
        {

            config = ConfigForm.getConfig();
            ResetListPlayer();
        }

        private void ResetListPlayer()
        {
            if (config != null)
            {
                listPlayer.Items.Clear();

                for (int i = 0; i < 5; i++)
                {
                    listPlayer.Items.Add("[" + i + "]번 " + config.nick[i]);
                }
            }
            lbMyInfo.Text = config.nick[0] + "님 화이팅~";

        }

        private void OpeningTimer_Tick(object sender, EventArgs e)
        {
            location[myPosition].openingHandedCard.Y += 50;
            Invalidate();
        }

        private void ThrowTimer_Tick(object sender, EventArgs e)
        {
            int position = gameInfo.round.cardStack[gameInfo.roundCardIdx].owner;

            if (Math.Abs(location[position].throwingCard.X - location[position].throwedCard.X) < 5 &&
                          Math.Abs(location[position].throwingCard.Y - location[position].throwedCard.Y) < 5)
            {
                soundCardDrop.Play();
                location[position].throwingCard = location[position].throwedCard;

                throwingTimer.Stop();
                throwedCards[position] = throwingCard;
                
                throwingCard = null;
            }
            else
            {
                location[position].throwingCard.X += throwingGap.X;
                location[position].throwingCard.Y += throwingGap.Y;

            }
            Invalidate();
        }

        private int GetCardIndex(Card card)
        {
            int result = 55;

            if (card != null)
            {
                // 에러 처리
                if (card.shape == Shape.Clover && card.number == 0) return -1;

                switch (card.shape)
                {
                    case Shape.Clover:
                        result = card.number;
                        break;
                    case Shape.Diamond:
                        result = card.number + 13;
                        break;
                    case Shape.Heart:
                        result = card.number + 13 * 2;
                        break;
                    case Shape.Spade:
                        result = card.number + 13 * 3;
                        break;
                    case Shape.Joker:
                        result = 54;
                        break;
                    default:
                        result = -1;
                        break;
                }
            }

            return result;
        }

        private bool CheckUsedJoker()
        {
            for (int i = 0; i < gameInfo.roundIdx; i++)
            {
                foreach (RoundCard rc in gameInfo.roundHistory[i].cardStack)
                {
                    if (rc.shape == Shape.Joker)
                        return true;
                }
            }
            return false;
        }

        private void Mighty_MouseClick(object sender, MouseEventArgs e)
        {
            if (log.IsDebugEnabled) log.Debug(String.Format("Mighty_MouseClick {0},{1}", e.X, e.Y));
            switch (gameStatus)
            {
                // 라운드마다 낼 카드 선택
                case ActualStatus.RoundTicEvent:
                    throwingCard = SelectHandedCard(e.X, e.Y);
                    if (throwingCard != null)
                    {
                        if (gameInfo.roundCardIdx == 0)
                        {
                            if (throwingCard.shape == gameInfo.jokerCall.shape && throwingCard.number == 3)
                            {
                                // 이전에 조커가 나오지 않았다면 
                                if (!CheckUsedJoker())
                                {
                                    DialogResult result = MessageBox.Show("조커콜 하시겠습니까?", "선택창", MessageBoxButtons.YesNo);
                                    if (result == DialogResult.Yes)
                                        throwingCard.cmdJokerCall = true;
                                }


                            }
                            else if (throwingCard.shape == Shape.Joker)
                            {
                                SelShape shapeForm = new SelShape();
                                shapeForm.ShowDialog();
                                throwingCard.cmdJokerTarget = shapeForm.selectedShape;
                            }
                        }
                        autoEvent.Set();
                        //Invalidate();
                    }
                    else
                    {
                        if (log.IsDebugEnabled) log.Debug(String.Format("    throwingCard == null"));
                    }
                    break;

                case ActualStatus.ChoiceCardEvent:
                    throwingCard = SelectHandedCard(e.X, e.Y);
                    if (throwingCard != null)
                    {
                        if (throwingCard.cmdRemove == false)
                        {
                            throwingCard.cmdRemove = true;
                            choicedCardNum++;
                            Invalidate();
                        }
                        else
                        {
                            throwingCard.cmdRemove = false;
                            choicedCardNum--;
                            Invalidate();
                        }
                    }
                    break;
            }
        }

        private HandedCard GetHandedCard(int tmpIndex)
        {
            HandedCard result = new HandedCard();
            int i = 0;
            //tmpIndex의 카드가 사용할 수 있는 카드인지 확인
            foreach (HandedCard c in gameInfo.myCards)
            {
                if (c != null)
                {
                    if (!c.isUsed)
                    {
                        if (i == tmpIndex)
                        {
                            result = c;
                            break;
                        }
                    }
                    else
                    {
                        tmpIndex++;
                    }
                }
                i++;
            }
            return result;
        }

        /// <summary>
        /// 현재카드가 올라간 카드인지 확인한다
        /// </summary>
        /// <param name="hc"></param>
        /// <returns></returns>
        private bool IsUpCard(HandedCard hc)
        {
            bool result = false;

            if (gameStatus == ActualStatus.ChoiceCardEvent)
            {
                if (hc.cmdRemove == true) result = true;
                else result = false;
            }
            else
            {
                if (hc.isPossible) result = true;
                else result = false;
            }
            return result;
        }


        /// <summary>
        /// 마우스가 선택한 카드를 찾는다
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        private HandedCard SelectHandedCard(int x, int y)
        {
            HandedCard result = null;
            HandedCard tmpResult = new HandedCard();
            int tmpIndex = (x - location[myPosition].handedCard.X) / 20;
            int lastCardIndex;
            // 손에 든 가장 마지막 카드인지 확인
            if (gameStatus == ActualStatus.ChoiceCardEvent)
                lastCardIndex = 12;
            else
                lastCardIndex = 10 - gameInfo.roundIdx - 1;

            if (tmpIndex >= lastCardIndex)
            {
                if (x < location[myPosition].handedCard.X + lastCardIndex * 20 + 70)
                    tmpIndex = lastCardIndex;
            }


            // Y 좌표 범위가 맞다면
            if (y > location[myPosition].handedCard.Y - 20 && y < location[myPosition].handedCard.Y + 95 - 20)
            {
                result = GetHandedCard(tmpIndex);

                // 현재 카드가 올라간 카드가 아니라면
                if (IsUpCard(result) == false)
                {
                    // Y좌표 범위가 카드 윗부분이라면
                    if (y > location[myPosition].handedCard.Y - 20 && y < location[myPosition].handedCard.Y)
                    {
                        // 앞의 세장까지 올라온(가능한) 카드 검사
                        for (int i = 0; i < 3; i++)
                        {
                            tmpResult = GetHandedCard(tmpIndex - i - 1);
                            if (IsUpCard(tmpResult))
                                return tmpResult;
                        }
                    }
                    if (gameStatus == ActualStatus.ChoiceCardEvent) return result;
                    else return null;
                }
            }
            return result;
        }

        /// <summary>
        /// 설정을 저장한다
        /// </summary>
        private void SaveConfig()
        {
            try
            {
                XmlSerializer xs = new XmlSerializer(typeof(Config));
                StringBuilder sb = new StringBuilder();
                StringWriter sw = new StringWriter(sb);
                xs.Serialize(sw, config);

                sw.Close();

                FileStream fs = new FileStream("config.cfg", FileMode.Create, FileAccess.Write);
                byte[] data = Encoding.Default.GetBytes(sb.ToString());

                for (int i = 0; i < data.Length; i++)
                {
                    fs.WriteByte(data[i]);
                }
                fs.Close();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
        }

        /// <summary>
        /// 게임마무리를 한다
        ///  * 점수를 반영한다
        ///  * 소리를 내준다
        /// </summary>
        /// <param name="score"></param>
        public void GameEnd(int score)
        {
            for (int i = 0; i < 5; i++) config.score[i] = gameInfo.players[i].score;
            SaveConfig();

            if (score > 0)
            {
                soundApplause.Stop();
                soundApplause.Play();
            }
            else
            {
                soundScream.Stop();
                soundScream.Play();
            }
            GameEnd();
        }

        /// <summary>
        /// 게임마무리를 한다
        ///  * 게임시작시 disable 시켰던 컨트롤를 enable 시켜준다
        /// </summary>
        public void GameEnd()
        {
            try
            {
                menuConfig.Enabled = true;
            }
            catch
            {
            }

            chkAiUsed.BackColor = Color.Transparent;
            chkAi.BackColor = Color.Transparent;

            showBtStart = true;
            enableComboMyAI = true;
            enableCombo1 = true;
            enableCombo2 = true;
            enableCombo3 = true;
            enableCombo4 = true;
            enableChkAI = true;
            enableChkAIUsed = true;

            UpdateBtStart();
            UpdateComboMyAI();
            UpdateCombo1();
            UpdateCombo2();
            UpdateCombo3();
            UpdateCombo4();
            UpdateChkAi();
            UpdateChkAiUsed();
        }

        private void Mighty_FormClosed(object sender, FormClosedEventArgs e)
        {
            if (gameThread != null) gameThread.Abort();
            gameThread = null;
            Application.Exit();
        }

        /// <summary>
        /// 던져진 카드의 위치를 초기화 한다
        ///  * 플레이어의 위치로 초기화
        /// </summary>
        public void InitAllThrowCard()
        {
            for (int i = 0; i < location.Length; i++)
            {
                location[i].ResetThrowCard();
                throwedCards[i] = null;
            }
        }

        /// <summary>
        /// 주공이 추가로 받은 카드를 선택한다음 선택완료 버튼을 클릭한다
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btChoiceOK_Click(object sender, EventArgs e)
        {
            if (choicedCardNum == 3)
            {
                autoEvent.Set();
                showBtChoiceOK = false;
                UpdateBtChoiceOK();
            }
            else
            {
                MessageBox.Show(String.Format("{0}개의 카드를 더 선택해 주세요.", 3 - choicedCardNum));
            }
        }

        /// <summary>
        /// 해당라운드의 점수카드를 이동시킨다
        ///  * pointCardTimer 사용
        /// </summary>
        public void MovePointCard()
        {
            int winnerPosition = gameInfo.roundHistory[gameInfo.roundIdx].winner;
            nowAlpha = 0;

            if (!CheckPointCard(gameInfo.roundIdx)) pointCardGoal = new Point(300, 200);
            else pointCardGoal = location[winnerPosition].scoreCard;
            pointCardTimer.Start();
        }


        /// <summary>
        /// 해당 라운드의 그림카드가 존재하는지 체크
        ///  * 존재하면 true 
        /// </summary>
        /// <param name="roundIdx"></param>
        /// <returns></returns>
        private bool CheckPointCard(int roundIdx)
        {
            foreach (Card c in gameInfo.roundHistory[roundIdx].cardStack)
            {
                if (c.number > 9) return true;
            }
            return false;
        }

        /// <summary>
        /// PointCardTimer_Tick에서 사용하는 전역변수
        /// </summary>
        Point[] pointCardGaps = new Point[5];

        /// <summary>
        /// 해당라운드의 점수카드의 이동을 보여준다
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void PointCardTimer_Tick(object sender, EventArgs e)
        {
            int i = 0;
            int tmp = 0;

            for (i = 0; i < 5; i++)
            {
                pointCardGaps[i].X = (pointCardGoal.X - location[i].throwingCard.X) / 5;
                pointCardGaps[i].Y = (pointCardGoal.Y - location[i].throwingCard.Y) / 5;
            }

            for (i = 0; i < 5; i++)
            {
                if (Math.Abs(location[0].throwingCard.X - pointCardGoal.X) < 10 &&
                      Math.Abs(location[0].throwingCard.Y - pointCardGoal.Y) < 10) tmp++;
            }

            if (tmp == 5)
            {
                InitAllThrowCard();
                pointCardTimer.Stop();
            }
            else
            {
                for (i = 0; i < 5; i++)
                {
                    location[i].throwingCard.X += pointCardGaps[i].X;
                    location[i].throwingCard.Y += pointCardGaps[i].Y;
                }
                nowAlpha += 10;
            }
            Invalidate();
        }

        private void comboAI1_SelectedValueChanged(object sender, EventArgs e)
        {
            if (comboAI1.Text == "") return;
            ModuleInfo m = (ModuleInfo)mightyModule.moduleHash[comboAI1.Text];
        }

        private void comboAI2_SelectedValueChanged(object sender, EventArgs e)
        {
            if (comboAI2.Text == "") return;
            ModuleInfo m = (ModuleInfo)mightyModule.moduleHash[comboAI2.Text];
        }

        private void comboAI3_SelectedValueChanged(object sender, EventArgs e)
        {
            if (comboAI3.Text == "") return;
            ModuleInfo m = (ModuleInfo)mightyModule.moduleHash[comboAI3.Text];
        }

        private void comboAI4_SelectedValueChanged(object sender, EventArgs e)
        {
            if (comboAI1.Text == "") return;
            ModuleInfo m = (ModuleInfo)mightyModule.moduleHash[comboAI4.Text];
        }

        private void comboMyAI_SelectedValueChanged(object sender, EventArgs e)
        {
            if (comboMyAI.Text == "") return;
            ModuleInfo m = (ModuleInfo)mightyModule.moduleHash[comboMyAI.Text];
        }

        public void SendGameInfoToClient(GameInfo g, int position)
        {

            XmlSerializer xs = new XmlSerializer(typeof(GameInfo));
            StringBuilder sb = new StringBuilder();
            StringWriter sw = new StringWriter(sb);
            xs.Serialize(sw, g);
            sw.Close();

            byte[] data = Encoding.Default.GetBytes(sb.ToString());
            //       SendData(data, );
        }

        /// <summary>
        /// 접속된 상대방 데이터 수신
        /// </summary>  
        public void Receive()
        {
            try
            {
                while (client != null && client.Connected)
                {
                    byte[] data = this.ReceiveData();
                    string msg = Encoding.Default.GetString(data);

                    XmlSerializer m = new XmlSerializer(typeof(GameInfo));
                    StringReader r = new StringReader(msg);
                    GameInfo gameinfo = (GameInfo)m.Deserialize(r);
                    r.Close();

                    DebugMsgAdd(msg);

                }
            }
            catch (Exception ex)
            {
                DebugMsgAdd(ex.Message);
            }
        }

        private byte[] ReceiveData()
        {
            try
            {
                int total = 0;
                int size = 0;
                int left_data = 0;
                int recv_data = 0;

                // 수신할 데이터 크기 알아내기   
                byte[] data_size = new byte[4];
                recv_data = this.client.Receive(data_size, 0, 4, SocketFlags.None);
                size = BitConverter.ToInt32(data_size, 0);
                left_data = size;

                byte[] data = new byte[size];
                // 서버에서 전송한 실제 데이터 수신
                while (total < size)
                {
                    recv_data = this.client.Receive(data, total, left_data, 0);
                    if (recv_data == 0) break;
                    total += recv_data;
                    left_data -= recv_data;
                }
                return data;
            }
            catch (Exception ex)
            {
                DebugMsgAdd(ex.Message);
                return null;
            }
        }

        /// <summary>
        /// 툴바의 '파일 > 환경설정' 메뉴 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void menuConfig_Click(object sender, EventArgs e)
        {
            ConfigForm cf = new ConfigForm();
            cf.ShowDialog();

            config = ConfigForm.getConfig();

            comboMyAI.Text = config.AI[0];
            comboAI1.Text = config.AI[1];
            comboAI2.Text = config.AI[2];
            comboAI3.Text = config.AI[3];
            comboAI4.Text = config.AI[4];

            ResetListPlayer();
            Invalidate();

        }

        /// <summary>
        /// 플레이어 카드를 무늬별로 나누어 정렬하기  
        /// </summary>
        /// <param name="cards"></param>
        /// <returns></returns>
        private HandedCard[] MakeCardGroup(HandedCard[] cards)
        {
            HandedCard[] result = new HandedCard[13];
            List<HandedCard> tmpCards = new List<HandedCard>();

            int pointer = 0;

            for (int i = 0; i < 5; i++)
            {
                for (int j = 0; j < 10; j++)
                {
                    if (cards[j].shape == (Shape)i && !cards[j].cmdRemove)
                        tmpCards.Add(gameInfo.myCards[j]);
                }
                tmpCards = SortCards(tmpCards);

                for (int k = 0; k < tmpCards.Count; k++)
                {
                    result[k + pointer] = tmpCards[k];
                }
                pointer += tmpCards.Count;
                tmpCards.Clear();
            }

            for (int m = 10; m < 13; m++)
            {
                if (cards.Length > m)
                    result[m] = cards[m];
            }

            return result;
        }

        /// <summary>
        /// 카드 정렬하기  
        /// </summary>
        /// <param name="cardList"></param>
        /// <returns></returns>
        private List<HandedCard> SortCards(List<HandedCard> cardList)
        {
            HandedCard tmpCard = new HandedCard();

            for (int i = 0; i < cardList.Count; i++)
            {
                for (int j = i + 1; j < cardList.Count; j++)
                {
                    if (cardList[i].number > cardList[j].number)
                    {
                        tmpCard = cardList[i];
                        cardList[i] = cardList[j];
                        cardList[j] = tmpCard;
                    }
                }
            }
            return cardList;
        }

        private void chkAiUsed_CheckedChanged(object sender, EventArgs e)
        {
            UpdateMyAIUsed();
        }


        /// <summary>
        /// 툴바의 '파일 > 다시 시작하기' 메뉴
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void 재시작ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            GameEnd();
            if (gameThread != null) gameThread.Abort();
            gameThread = null;
        }


        /// <summary>
        /// 툴바의 '파일 > 종료하기' 메뉴
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void 종료ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (gameThread != null)
            {
                gameThread.Abort();
                this.Close();
            }
        }

        private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
        {
            About about = new About();
            about.ShowDialog();
        }

        #region IVisual 멤버

        public void SetGameInfo(GameInfo g)
        {
            gameInfo = g;

            if (gameInfo.myCards.Length > 1)
            {
                HandedCard[] myCards = MakeCardGroup(gameInfo.myCards);
                gameInfo.myCards = myCards;
            }
        }
  
        public object Visualize(ActualStatus s, int position)
        {
            Object result = null;
            gameStatus = s;

            if (log.IsDebugEnabled) log.Debug(String.Format("Visualize {0}", gameStatus));
            switch (gameStatus)
            {
                case ActualStatus.GameStartEvent:
                    SetTalkMsg("", -1);
                    if (playingBoard.gameMode == GameMode.Server || playingBoard.gameMode == GameMode.Offline) Thread.Sleep(100);
                    result = null;
                    break;

                case ActualStatus.BettingReadyEvent:
                    openingTimer.Start();
                    soundShuffle.Stop();
                    soundShuffle.Play();
                    gameStart = true;
                    if (playingBoard.gameMode == GameMode.Server || playingBoard.gameMode == GameMode.Offline) Thread.Sleep(800);
                    break;

                case ActualStatus.BettingTicEvent:
                    BettingForm bf = new BettingForm(gameInfo, ActualStatus.BettingTicEvent);
                    if (myAiUsed)
                    {
                        Thread.Sleep(800);
                    } else {
                        bf.ShowDialog();
                    }
                    return bf.betting;

                case ActualStatus.BettingTalkEvent:
                    if (gameInfo.betting != null && gameInfo.betting.limit > 12) tsBettingInfo.Text = String.Format("{0}님이 {1} ({2})을 부르셨습니다", gameInfo.players[gameInfo.betting.position], gameInfo.betting.giruda, gameInfo.betting.limit);
                    Invalidate();
                    if (playingBoard.gameMode == GameMode.Server || playingBoard.gameMode == GameMode.Offline) Thread.Sleep(800);
                    return result;
                
                case ActualStatus.BettingCompleteEvent:
                    Invalidate();
                    if (playingBoard.gameMode == GameMode.Server || playingBoard.gameMode == GameMode.Offline) Thread.Sleep(800);
                    return result;
                
                case ActualStatus.ChoiceCardEvent:
                    if (myAiUsed)
                    {
                        Thread.Sleep(800);
                    }
                    else
                    {
                        showBtChoiceOK = true;
                        UpdateBtChoiceOK();
                        Invalidate();
                        autoEvent.WaitOne(playingBoard.timeOutRound, false);
                        result = gameInfo.myCards;
                    }
                    break;
                
                case ActualStatus.LastBettingEvent:
                    if (myAiUsed)
                    {
                        Thread.Sleep(800);
                    }
                    else
                    {
                        bf = new BettingForm(gameInfo, ActualStatus.LastBettingEvent);
                        Invalidate();
                        if (MessageBox.Show("공약은 그대로입니까?", "공약확인", MessageBoxButtons.YesNo) == DialogResult.No)
                        {
                            bf.ShowDialog();
                            result = bf.betting;
                            return bf.betting;
                        }
                        result = gameInfo.betting;
                    }
                    break;

                case ActualStatus.ChoiceFriendEvent:
                    if (myAiUsed)
                    {
                        Thread.Sleep(800);
                    }
                    else
                    {
                        FriendForm ff = new FriendForm(gameInfo);
                        ff.ShowDialog();
                        result = ff.friendPlayer;
                    }
                    break;

                case ActualStatus.ChoiceFriendTalkEvent:
                    String strFriend = gameInfo.friend.GetFriendCondition(gameInfo);
                    tsBettingInfo.Text = String.Format("공약 : {0} {1} ({2})", gameInfo.betting.giruda, gameInfo.betting.limit, strFriend);
                    SetTalkMsg(string.Format("{0}\r\n{1}", gameInfo.betting.giruda, strFriend), gameInfo.attackPlayer.position);
                    Invalidate();
                    if (playingBoard.gameMode == GameMode.Server || playingBoard.gameMode == GameMode.Offline) Thread.Sleep(2000);
                    return result;
                
                case ActualStatus.RoundReadyEvent:
                    InitAllThrowCard();
                    talkMsg_str = "";
                    if (playingBoard.gameMode == GameMode.Server || playingBoard.gameMode == GameMode.Offline) Thread.Sleep(300);
                    break;
                
                case ActualStatus.RoundTicEvent:
                    if (!myAiUsed) {
                        Invalidate();
                        autoEvent.WaitOne(playingBoard.timeOutRound, false);
                        result = throwingCard;
                    }
                    break;

                case ActualStatus.RoundCardThrowEvent:
                    RoundCard r = gameInfo.round.cardStack[gameInfo.roundCardIdx];
                    ThrowCard(r);
                    if (playingBoard.gameMode == GameMode.Server || playingBoard.gameMode == GameMode.Offline) Thread.Sleep(600);
                    break;

                case ActualStatus.RoundCompleteEvent:
                    if (CheckPointCard(gameInfo.roundIdx))
                    {
                        soundGetPoint.Stop();
                        soundGetPoint.Play();
                    }
                    MovePointCard();
                    if (playingBoard.gameMode == GameMode.Server || playingBoard.gameMode == GameMode.Offline) Thread.Sleep(600);
                    break;
                
                case ActualStatus.GameEndEvent:
//                    if (playingBoard.gameMode == GameMode.Server || playingBoard.gameMode == GameMode.Offline) Thread.Sleep(600);

                    String endingMsg = String.Format("주공카드개수: {0}\r\n", 20 - gameInfo.score.cntDependerCard);
                    endingMsg += String.Format("공격팀의점수: {0}\r\n", gameInfo.score.baseScore);
                    endingMsg += String.Format("당신의점수: {0}\r\n", gameInfo.score.playerScore[0]);

                    int myPoint = gameInfo.score.playerScore[0];
                    if (myPoint > 0) endingMsg += String.Format("\r\n이겼습니다.\r\n");
                    else if (myPoint < 0) endingMsg += String.Format("\r\n졌습니다.\r\n");
                    else endingMsg += String.Format("\r\n동점입니다.\r\n");
                    SetTalkMsg(endingMsg, -1);

                    GameEnd(myPoint);
                    if (playingBoard.gameMode == GameMode.Server || playingBoard.gameMode == GameMode.Offline) Thread.Sleep(500);
                    break;
            }
            Invalidate();
            return result; 
        }


        public void Talk(String msg, int position)
        {
            SetTalkMsg(msg, position);
        }

        public void Chat(String msg, int position)
        {
        }

        #endregion


        #region IDebug 멤버

        public void ClearMessage()
        {
            DebugMsgClear();
        }

        public void ClearSummary()
        {
            SummaryMsgClear();
        }

        public void AddMessage(string msg)
        {
            DebugMsgAdd(msg);
        }

        public void AddSummary(string msg)
        {
            SummaryMsgAdd(msg);
        }

        #endregion

        private void tabControl1_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (tabControl1.SelectedIndex == 0) showChatView = false;
            else if (tabControl1.SelectedIndex == 1) showChatView = true;
        }
    }

    /// <summary>
    /// 리소스에 접근 
    /// </summary>
    internal abstract class Resources
    {
        private static ResourceManager images;

        static Resources()
        {
            images = new ResourceManager("mighty.Images", Assembly.GetExecutingAssembly());
        }

        public static Image GetImage(string imgName)
        {
            return (Image)images.GetObject(imgName, CultureInfo.InvariantCulture);
        }
    }

    public enum StrType
    {
        normal,
        shadow,
        timer

    }

    public class PlayerLocation
    {
        public Point handedCard;
        public Point openingHandedCard;

        public Point throwedCard;
        public Point throwingCard;
        private Point throwingStartCard;
        public Point scoreCard;
        public Point combo;
        public Point name;
        public int position;

        public PlayerLocation(int num)
        {
            position = num;

            switch (num)
            {
                case 0: // 플레이어 자신을 의미
                    handedCard = new Point(210, 545);
                    scoreCard = new Point(210, 425);
                    throwedCard = new Point(315, 280);
                    openingHandedCard = new Point(210, -100);
                    throwingCard = new Point(210, 700);
                    throwingStartCard = new Point(210, 700);
                    break;
                case 1:
                    scoreCard = new Point(30, 310);
                    throwedCard = new Point(250, 230);
                    throwingCard = new Point(-100, 330);
                    throwingStartCard = new Point(-100, 330);
                    break;
                case 2:
                    scoreCard = new Point(30, 80);
                    throwedCard = new Point(280, 180);
                    throwingCard = new Point(-100, 80);
                    throwingStartCard = new Point(-100, 80);
                    break;
                case 3:
                    scoreCard = new Point(470, 80);
                    throwedCard = new Point(370, 190);
                    throwingCard = new Point(800, 70);
                    throwingStartCard = new Point(800, 70);
                    break;
                case 4:
                    scoreCard = new Point(470, 310);
                    throwedCard = new Point(390, 240);
                    throwingCard = new Point(800, 330);
                    throwingStartCard = new Point(800, 330);
                    break;
            }

            combo.X = scoreCard.X;
            combo.Y = scoreCard.Y - 26;

            name.X = scoreCard.X;
            name.Y = scoreCard.Y - 20;

        }

        public void ResetThrowCard()
        {
            throwingCard = throwingStartCard;
            if (position == 0) openingHandedCard.Y = -100;
        }
    }
}